{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "configured-sweden",
   "metadata": {},
   "source": [
    "# Multi-Client Example\n",
    "\n",
    "## Introduction\n",
    "\n",
    "This example shows how to connect multiple `BeamNGpy` instances to the simulator and have them control different vehicles. For demonstration purposes, both instances are housed in the same Python process, but the same example could be used from an entirely different process or machine as well.\n",
    "\n",
    "## Scenario\n",
    "\n",
    "The scenario will be a rather simple one: there are two vehicles on the `smallgrid` map (an infinite flat plane) and one vehicle will randomly drive around, controlled by client A, with another vehicle, controlled by client B, mimicking A's behavior.\n",
    "\n",
    "## Setup\n",
    "\n",
    "Contrary to other examples, we will be using two instances of the `BeamNGpy` class representing two clients A and B. In our case, client A will create the scenario containing both vehicles, but client B will later connect and control one of the vehicles while A controls the other. The classes involved in this are:\n",
    "\n",
    "* `BeamNGpy`: Two instances of this will be used to implement client A and B\n",
    "* `Scenario`: Client A will create a scenario for both clients to play in\n",
    "* `Vehicle`: Used to specify and control vehicles involved in the scenario\n",
    "* `Electrics`: A sensor used to inspect interal systems of the vehicle\n",
    "\n",
    "The code starts with importing the respective classes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "aware-grammar",
   "metadata": {},
   "outputs": [],
   "source": [
    "import random\n",
    "\n",
    "import numpy as np\n",
    "\n",
    "from beamngpy import BeamNGpy, Scenario, Vehicle\n",
    "from beamngpy.sensors import Electrics"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "tribal-arrival",
   "metadata": {},
   "source": [
    "Now we set up our first client who will create the scenario for both clients. Client A will also launch the simulator."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "crude-hampton",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<beamngpy.beamng.BeamNGpy at 0x29f458e58b0>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "client_a = BeamNGpy('localhost', 64256)\n",
    "client_a.open()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "solid-soundtrack",
   "metadata": {},
   "source": [
    "With the simulator running, we can start setting up our scenario. It will contain two vehicles, the one controlled by Client A being placed in front of the one later controlled by client B."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "operational-market",
   "metadata": {},
   "outputs": [],
   "source": [
    "scenario = Scenario('smallgrid', 'tag')\n",
    "av_a = Vehicle('vehicleA', model='etk800')\n",
    "av_b = Vehicle('vehicleB', model='etk800')\n",
    "scenario.add_vehicle(av_a, pos=(0, -10, 0))\n",
    "scenario.add_vehicle(av_b)\n",
    "scenario.make(client_a)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "global-intellectual",
   "metadata": {},
   "source": [
    "## Running\n",
    "\n",
    "The scenario is now made, meaning the required files have been generated and can be loaded in the simulator:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "endangered-hampton",
   "metadata": {},
   "outputs": [],
   "source": [
    "client_a.load_scenario(scenario)\n",
    "client_a.start_scenario()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "smaller-spanish",
   "metadata": {},
   "source": [
    "Now we will set up our second client and connect it to the running simulator. The client will first connect, then query the running scenario, and retrieve currently active vehicles. They will then find the vehicle meant for Client B and connect to it. Note that `client_b` is opened with two flags `launch=False` and `deploy=False` meaning it will not launch its own BeamNG.tech process and not deploy the Lua files necessary for communication as Client A has already done so."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "secure-breakfast",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tag\n"
     ]
    }
   ],
   "source": [
    "client_b = BeamNGpy('localhost', 64256)\n",
    "client_b.open(launch=False, deploy=False)\n",
    "running_scenario = client_b.get_current_scenario()\n",
    "print(running_scenario.name)\n",
    "active_vehicles = client_b.get_current_vehicles()\n",
    "bv_a = active_vehicles['vehicleA']\n",
    "bv_b = active_vehicles['vehicleB']\n",
    "# B attaches their own sensor to get the current controls of A\n",
    "bv_a.attach_sensor('electrics', Electrics())\n",
    "bv_a.connect(client_b)\n",
    "bv_b.connect(client_b)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "relevant-elevation",
   "metadata": {},
   "source": [
    "Two clients are now connected to the running simulation and both vehicles. What follows is the main loop of the scenario, where Client A sends random steering inputs to their vehicle and Client B checks how A's vehicle is driving using the electrics sensor and sends the same inputs to their vehicle."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "quick-stuff",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.17316560355877775 -0.08137247713993863\n",
      "0.2245978039112846 -0.4669812479331782\n",
      "0.6309506494197058 0.2990009632308111\n",
      "0.8662440221625067 -0.8740858308898218\n",
      "0.22348886211721652 -0.7906601926514055\n",
      "0.28467718400361963 -0.557201380577362\n",
      "0.9062191595767862 -0.1919533939659307\n",
      "0.29767996666474217 0.1362717779570709\n",
      "0.1194594550230087 0.8864501474166042\n",
      "0.9660245397509041 0.7024685659545233\n",
      "0.1918685051500344 0.13618006004285987\n",
      "0.3873766034334287 0.23259978071938658\n",
      "0.09708406341805664 -0.6515072093174388\n",
      "0.500591338395343 -0.15141255143421536\n",
      "0.021660823643308458 0.40209764032728046\n",
      "0.30375545984087304 0.30091720413308726\n",
      "0.48781119851984633 0.35782509734987167\n",
      "0.1682884358807526 -0.3554365707400838\n",
      "0.3095892955505989 0.2765067242028339\n",
      "0.22299518082737402 0.03622262657787638\n",
      "0.5967267475752369 -0.0016975713076148313\n",
      "0.8557363317327791 -0.1202967102304329\n",
      "0.5719057059545953 -0.6392819679388321\n",
      "0.8139803389894057 -0.5460076795431187\n",
      "0.20880170156772415 0.36301691372873035\n",
      "0.9674076040194005 0.7326056552339151\n",
      "0.9467199797108792 0.24451178160140877\n",
      "0.794248415994073 -0.2537927599566528\n",
      "0.680518366427005 -0.7307248410595731\n",
      "0.753685939391301 -0.7923269493735151\n",
      "0.6017284532455062 0.530150961238319\n",
      "0.5121956597486441 -0.864170132974869\n",
      "0.3188371265268317 -0.7026848894252735\n",
      "0.12492533116950322 0.6645358961384494\n",
      "0.8759048882442168 0.24749302992532363\n",
      "0.22057028615274288 0.3952201285027833\n",
      "0.1300232252266631 -0.836538171278724\n",
      "0.05830000506352473 -0.19982850769895139\n",
      "0.9305435105059597 0.16681022079455793\n",
      "0.9109102313865115 -0.6919229532459651\n",
      "0.3428337343767147 -0.15512411397829023\n",
      "0.5818369400522706 0.5860553686761766\n",
      "0.9098987078571168 0.1916649675441583\n",
      "0.13740593758344788 0.22629713700787835\n",
      "0.8303843398618348 0.14179595777338214\n",
      "0.3342309973054757 -0.10675987393663385\n",
      "0.4674668624994278 -0.26100396745820187\n",
      "0.12748243638177525 0.24678091079408176\n",
      "0.8670340625453431 -0.12847125921539163\n",
      "0.29910402426151805 -0.495717284769522\n",
      "0.7096040584536264 -0.07496738607653779\n",
      "0.07719409282907319 0.11845634611534103\n",
      "0.3406501133908957 0.3380342512299911\n",
      "0.08378610964828825 -0.3072723265911765\n",
      "0.47619790522406413 0.13146184313389747\n",
      "0.6029155519067227 0.3597507847787299\n",
      "7.338745909868383e-05 0.6816250615536207\n",
      "0.0739477375925679 0.4962774038179333\n",
      "0.052191707688157174 0.7553770390523872\n",
      "0.3827892179225434 -0.5334786041934187\n",
      "0.430708132068539 0.4289948553752103\n",
      "0.9235449516649626 0.22384779168689004\n",
      "0.4710691290204557 -0.3936861633092757\n",
      "0.2812198724543121 0.7062405182153901\n",
      "0.34739116334094255 0.8522089210394048\n",
      "0.6834696097411856 0.6481648007627293\n",
      "0.9888714659708044 0.02097936621054737\n",
      "0.021846238924668082 0.12406587469940533\n",
      "0.16415060934405212 -0.7307380113262407\n",
      "0.7683009318981635 -0.2655698413995573\n",
      "0.8324227305944752 0.1349700982753574\n",
      "0.28957200229433977 0.8099049545082652\n",
      "0.19463379352601007 0.13369020999063796\n",
      "0.8823253190382385 -0.2091600598541183\n",
      "0.7870250173572011 -0.492832365444047\n",
      "0.6338920531970424 -0.6585066958578617\n",
      "0.18104095213000193 0.21369309956360263\n",
      "0.9574497112708857 0.01598432507863054\n",
      "0.6656414702246555 -0.7903932517646671\n",
      "0.18366754259319762 -0.3813726962377406\n",
      "0.13802600541869492 -0.34720207326699837\n",
      "0.9463768255749131 -0.06473542193902146\n",
      "0.5491323620702032 -0.2626066279322087\n",
      "0.3010406081189 0.08878267060987635\n",
      "0.36625037671922 0.27058390935357635\n",
      "0.7957240943198918 -0.7475316174427229\n",
      "0.5203673103196699 -0.5423845537544026\n",
      "0.25478138693127206 0.38902481432332003\n",
      "0.5505980095358202 0.04560980162612252\n",
      "0.07753477699154099 -0.4667199796663138\n",
      "0.06596573791555027 -0.20808170955919608\n",
      "0.2553421162268382 -0.15774064033323026\n",
      "0.28235357373154113 -0.6577306873626123\n",
      "0.033294153275152394 -0.20307652761150013\n",
      "0.11822195239020028 -0.4274383365022518\n",
      "0.6897869479040228 -0.7739212078507444\n",
      "0.2436257016961101 0.15694892680960273\n",
      "0.8990577808930228 -0.2984981069716282\n",
      "0.8500321110755841 0.4922225723721988\n",
      "0.9452650987213884 -0.6830879343237883\n",
      "0.2433061587410772 0.6123519221342523\n",
      "0.7531402578019843 0.0849459696752764\n",
      "0.13016581294846186 0.511401316994085\n",
      "0.9768685317231097 0.17785968931255242\n",
      "0.7665846060061722 -0.19802358723575703\n",
      "0.5153078431882395 -0.38439487932412053\n",
      "0.9650030866988821 -0.7486790112016897\n",
      "0.6355156336752837 -0.11113949549130486\n",
      "0.07499248497819677 0.7359562370209418\n",
      "0.3201909518106192 0.28706097754548754\n",
      "0.023651526669540845 0.08301685726881206\n",
      "0.9168049673153583 -0.7592208913599879\n",
      "0.7560919054535395 -0.16873176197774226\n",
      "0.6439611248953925 -0.14416809949132126\n",
      "0.17670619442791358 -0.5439556982593277\n",
      "0.2576305223679646 -0.07086403409499431\n",
      "0.739476400240737 -0.3239211377152319\n",
      "0.7559715702800173 0.2526329178781598\n",
      "0.02905562698622477 -0.07860263427784343\n",
      "0.3553516750401283 -0.24521036596614165\n"
     ]
    },
    {
     "ename": "ConnectionResetError",
     "evalue": "[WinError 10054] An existing connection was forcibly closed by the remote host",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mConnectionResetError\u001b[0m                      Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-6-a51cafd8ee87>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m     18\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     19\u001b[0m \u001b[0mclient_b\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mclose\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 20\u001b[1;33m \u001b[0mclient_a\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mclose\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32md:\\workgit\\beamngpy\\src\\beamngpy\\beamng.py\u001b[0m in \u001b[0;36mclose\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m    443\u001b[0m             \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mscenario\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    444\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 445\u001b[1;33m         \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mkill_beamng\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    446\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    447\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0mhide_hud\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32md:\\workgit\\beamngpy\\src\\beamngpy\\beamng.py\u001b[0m in \u001b[0;36mkill_beamng\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m    264\u001b[0m         \u001b[0mKills\u001b[0m \u001b[0mthe\u001b[0m \u001b[0mrunning\u001b[0m \u001b[0mBeamNG\u001b[0m\u001b[1;33m.\u001b[0m\u001b[1;33m*\u001b[0m \u001b[0mprocess\u001b[0m\u001b[1;33m.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    265\u001b[0m         \"\"\"\n\u001b[1;32m--> 266\u001b[1;33m         \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mquit_beamng\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    267\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    268\u001b[0m         \u001b[1;32mif\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mprocess\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32md:\\workgit\\beamngpy\\src\\beamngpy\\beamngcommon.py\u001b[0m in \u001b[0;36mack_wrapped\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m     57\u001b[0m         \u001b[1;32mdef\u001b[0m \u001b[0mack_wrapped\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     58\u001b[0m             \u001b[0mret\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mfun\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 59\u001b[1;33m             \u001b[0mresp\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0margs\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mrecv\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     60\u001b[0m             \u001b[1;32mif\u001b[0m \u001b[0mresp\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'type'\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m!=\u001b[0m \u001b[0mack_type\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     61\u001b[0m                 raise BNGError('Wrong ACK: {} != {}'.format(ack_type,\n",
      "\u001b[1;32md:\\workgit\\beamngpy\\src\\beamngpy\\beamng.py\u001b[0m in \u001b[0;36mrecv\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m    408\u001b[0m             \u001b[0mThe\u001b[0m \u001b[0mdata\u001b[0m \u001b[0mreceived\u001b[0m\u001b[1;33m.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    409\u001b[0m         \"\"\"\n\u001b[1;32m--> 410\u001b[1;33m         \u001b[1;32mreturn\u001b[0m \u001b[0mrecv_msg\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mskt\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    411\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    412\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0mopen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mextensions\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m*\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mlaunch\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mTrue\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mdeploy\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mTrue\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mopts\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32md:\\workgit\\beamngpy\\src\\beamngpy\\beamngcommon.py\u001b[0m in \u001b[0;36mrecv_msg\u001b[1;34m(skt)\u001b[0m\n\u001b[0;32m    168\u001b[0m         \u001b[0mThe\u001b[0m \u001b[0mdecoded\u001b[0m \u001b[0mmessage\u001b[0m\u001b[1;33m.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    169\u001b[0m     \"\"\"\n\u001b[1;32m--> 170\u001b[1;33m     \u001b[0mlength\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mskt\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mrecv\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m16\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    171\u001b[0m     \u001b[0mlength\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mstr\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mlength\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'ascii'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    172\u001b[0m     \u001b[0mbuf\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mbytearray\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mConnectionResetError\u001b[0m: [WinError 10054] An existing connection was forcibly closed by the remote host"
     ]
    }
   ],
   "source": [
    "# Focus simulator on second vehicle because it's the more interesting one to see\n",
    "client_a.switch_vehicle(av_b)\n",
    "\n",
    "for _ in range(120):\n",
    "    # Client A sending control inputs to their vehicle connection\n",
    "    av_a.control(throttle=random.random(), steering=(random.random() * 2 - 1))\n",
    "    \n",
    "    # Client B updating the electrics sensor of A's vehicle\n",
    "    bv_a.poll_sensors()\n",
    "    \n",
    "    throttle = bv_a.sensors['electrics'].data['throttle_input']\n",
    "    steering = bv_a.sensors['electrics'].data['steering_input']\n",
    "    print(throttle, steering)\n",
    "    bv_b.control(throttle=throttle, steering=steering)\n",
    "    \n",
    "    # Client A now advancing the simulation 60 steps\n",
    "    client_a.step(60)\n",
    "\n",
    "client_b.close()\n",
    "client_a.close()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
